﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DbGenerator.Model.units.table;
using DbGenerator.Model.units.column;
using DbGenerator.Model.units.constraint.types;
using DbGenerator.Model.units.column.relations;
using DbGenerator.Utilities;

namespace DbGenerator.Model.units.database
{
    public class TDatabase
    {
        public TDatabase(string name)
        {
            _Tables = new List<TTable>();
            _DatabaseName = name;
        }

        #region methods
        public void AddTable(TTable t)
        {
            _Tables.Add(t);
        }
        public void AddRangeTable(TTable[] tArr)
        {
            _Tables.AddRange(tArr);
        }
        public string RelationsScript()
        {
            StringBuilder sb = new StringBuilder();

            TColumn[] FKColumnList = CollectAllFKColumns();

            int columnCount = FKColumnList.Length;
            for (int j = 0; j < columnCount; j++)
            {
                TColumn curFKCol = FKColumnList[j];
                sb.AppendFormat("\nALTER TABLE [{0}].[{1}] WITH CHECK ADD  CONSTRAINT [{2}] FOREIGN KEY({3})"
                    + "\nREFERENCES [{4}].[{5}] ({6})"
                     , curFKCol.BaseTable.TableSchema
                    , curFKCol.BaseTable.TableSign.TableName
                    , SDbGenerationUtilities.ConstructFKConstraintName(curFKCol.ColumnInfo.ColumnName
                        , curFKCol.FKConstraint.ReferencedTableName)
                    , curFKCol.ColumnInfo.ColumnName
                    , curFKCol.Connector.ReferencedTable.TableSchema
                    , curFKCol.Connector.ReferencedTable.TableSign.TableName
                    , curFKCol.Connector.ReferencedTable.SelectColumnScript(EConstraintType.PRIMARYKEY));
            }
            return sb.ToString();
        }
        public TColumn[] CollectAllPKColumns()
        {
            List<TColumn> allColumns = new List<TColumn>();
            foreach (TTable t in _Tables)
            {
                int columnCount = t.Columns.Count;
                for (int i = 0; i < columnCount; i++)
                {
                    if (t.Columns[i].PKConstraint != null)
                        allColumns.Add(t.Columns[i]);
                }
            }
            return allColumns.ToArray();
        }
        public TColumn[] CollectAllFKColumns()
        {
            List<TColumn> allColumns = new List<TColumn>();
            foreach (TTable t in _Tables)
            {
                int columnCount = t.Columns.Count;
                for (int i = 0; i < columnCount; i++)
                {
                    if (t.Columns[i].FKConstraint != null)
                        allColumns.Add(t.Columns[i]);
                }
            }
            return allColumns.ToArray();
        }
        #endregion

        #region properties
        private string _DatabaseName;
        public string DatabaseName
        {
            get { return _DatabaseName; }
            //set { _DatabaseName = value; }
        }

        private List<TTable> _Tables;
        public List<TTable> Tables
        {
            get { return _Tables; }
            //set { _Tables = value; }
        }
        #endregion

        #region TableSequence methods
        private List<TTable> _SortedTableList;
        public TTable[] GetDeleteSafeTableSequence()
        {
            if (_SortedTableList == null)
                _SortedTableList = new List<TTable>();
            _SortedTableList.Clear();
            TTable[] cloneTables = new TTable[_Tables.Count];
            _Tables.CopyTo(cloneTables, 0);
            GenerateDeleteSequenceRecursive(new List<TTable>(cloneTables));
            return _SortedTableList.ToArray();
        }
        public TTable[] GetInsertSafeTableSequence()
        {
            if (_SortedTableList == null)
                _SortedTableList = new List<TTable>();
            _SortedTableList.Clear();
            TTable[] cloneTables = new TTable[_Tables.Count];
            _Tables.CopyTo(cloneTables, 0);
            GenerateDeleteSequenceRecursive(new List<TTable>(cloneTables));
            _SortedTableList.Reverse();
            return _SortedTableList.ToArray();
        }
        private void GenerateDeleteSequenceRecursive(List<TTable> tables)
        {
            int tableCount = tables.Count;
            if (tables.Count == 0)
                return;
            for (int i = 0; i < tableCount; i++)
            {
                if (TableIsIndependant(tables, i))
                {
                    _SortedTableList.Add(tables[i]);
                    tables.RemoveAt(i);
                    GenerateDeleteSequenceRecursive(tables);
                    return;
                }
            }
        }
        private bool TableIsIndependant(List<TTable> tables, int tableIndex)
        {
            TTable tableChecked = tables[tableIndex];
#warning No multiple pk supported!
            TColumn checkedPKColumn = null;
            foreach (TColumn pk in tableChecked.SelectColumn(EConstraintType.PRIMARYKEY))
                checkedPKColumn = pk;
            if (checkedPKColumn == null)
                return true;
            int tableCount = tables.Count;
            for (int i = 0; i < tableCount; i++)
            {
                if (i == tableIndex)
                    continue;
                foreach (TColumn fkCol in tables[i].SelectColumn(EConstraintType.FOREIGNKEY))
                {
                    if (fkCol.Connector.ReferencedTable.SelectColumn(EConstraintType.PRIMARYKEY)[0].Equals(checkedPKColumn))
                        return false;
                }
            }
            return true;
        }
        #endregion

    }
}
